/*
 *
 * Copyright (C) 2008 Jose Javier García Zornoza.
 * 
 * Este es un programa libre; puedes distribuirlo y/o modificarlo bajo
 * los términos de la versión 2 (o cualquier versión posterior) de la Licencia 
 * Pública General de la Fundación de Software Libre.
 * Este programa se distribuye con la esperanza  de poder ser útil, pero sin
 * NINGUNA GARANTIA; tampoco la garantía implícita de COMERCIALIZACION o DEDICACIÓN
 * A UN PROPOSITO PARTICULAR. Ver la licencia GNU GPL para más detalles.
 * Deberías haber recibido una copia de la licencia GNU GPL con este programa;
 * en caso contrario puedes obtener una copia visitando el sitio web www.fsf.org
 * o escribiendo a la Fundación de Software Libre, Inc., 59 Temple 
 * Place - Suite 330, Boston, MA  02111-1307, USA. 
 *
 */
package org.gruposp2p.dnie.crypto;

import java.io.ByteArrayOutputStream;
import java.security.PrivateKey;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.signature.ObjectContainer;
import org.apache.xml.security.signature.SignedInfo;
import org.apache.xml.security.signature.XMLSignature;
import org.apache.xml.security.transforms.Transforms;
import org.apache.xml.security.utils.Constants;
import org.apache.xml.security.utils.XMLUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.security.cert.X509Certificate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Signer {

    private static Logger logger = LoggerFactory.getLogger(Signer.class);
    
    public static final String ID_SIGNED_DATA = "SignedData";

    static {
        org.apache.xml.security.Init.init();
    }
   
    public static String createEnvelopedSignature(Document request, 
            X509Certificate certificado, PrivateKey privateKey)
            throws Exception {
        Constants.setSignatureSpecNSprefix("");
        javax.xml.parsers.DocumentBuilderFactory dbf =
            javax.xml.parsers.DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);        
        Element root = (Element) request.getFirstChild();
        request.setXmlStandalone(false);
        XMLSignature xmlSignature = new XMLSignature(request, null,
            XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1);
        SignedInfo signedInfo = xmlSignature.getSignedInfo();
        root.appendChild(xmlSignature.getElement()); 
        Transforms transforms = new Transforms(request);
        transforms.addTransform(Transforms.TRANSFORM_ENVELOPED_SIGNATURE);
        transforms.addTransform(Transforms.TRANSFORM_C14N_WITH_COMMENTS);
        xmlSignature.addDocument("", transforms, Constants.ALGO_ID_DIGEST_SHA1);
        xmlSignature.addKeyInfo(certificado);
        xmlSignature.sign(privateKey);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        XMLUtils.outputDOMc14nWithComments(request, baos);
        return baos.toString();
    }
        
    public static Document createEnvelopingSignatureDoc(Document request, 
            X509Certificate certificado, PrivateKey privateKey)
            throws ParserConfigurationException, XMLSecurityException, Exception {
        javax.xml.parsers.DocumentBuilderFactory dbf =
            javax.xml.parsers.DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        javax.xml.parsers.DocumentBuilder db = dbf.newDocumentBuilder();
        Document document = db.newDocument();
        document.setXmlStandalone(false);
        XMLSignature xmlSignature = new XMLSignature(document, null, 
                XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1);
        Node nodeMsg = request.cloneNode(true);
        NodeList list = request.getChildNodes();
        Element elementMsg = (Element)list.item(0);
        document.appendChild(xmlSignature.getElement());
        ObjectContainer objectContainer = new ObjectContainer(document);
        Node nodoDuplicado = document.importNode(elementMsg, true);
        objectContainer.appendChild(nodoDuplicado);
        objectContainer.setId(ID_SIGNED_DATA);
        xmlSignature.appendObject(objectContainer);
        Transforms transforms = new Transforms(document);
        transforms.addTransform(Transforms.TRANSFORM_C14N_WITH_COMMENTS);
        xmlSignature.addDocument("#" + ID_SIGNED_DATA,
                transforms, Constants.ALGO_ID_DIGEST_SHA1);
        if (certificado != null) {        
            xmlSignature.addKeyInfo(certificado);
            xmlSignature.sign(privateKey);
        } else {
            throw new Exception("No se ha podido extraer el certificado del documento.");
        }
        return document;        
    }
    
    public static String createEnvelopingSignature(
            Document request, X509Certificate certificado, PrivateKey privateKey)
            throws ParserConfigurationException, XMLSecurityException, Exception {
        Document document = 
                createEnvelopingSignatureDoc(request, certificado, privateKey);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        XMLUtils.outputDOMc14nWithComments(document, baos);
        return baos.toString();
    }

     public static Document createEnvelopingSignatureDocument(
            Document request, X509Certificate certificado, PrivateKey privateKey)
            throws ParserConfigurationException, XMLSecurityException, Exception {
        Document document =
                createEnvelopingSignatureDoc(request, certificado, privateKey);
        return document;
    }
       
}
